//using UnityEngine;
using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace MxNetwork
{
    public class MxNwkConnecterDesc
    {
        public MxNwkConnecterDesc(string IP, int Port, bool IsEncrypt, uint LocalType, uint LocalID, uint RemoteType, uint RemoteID)
        {
            m_IP = IP;
            m_Port = Port;
            m_IsEncrypt = IsEncrypt;
            m_LocalType = LocalType;
            m_LocalID = LocalID;
            m_RemoteType = RemoteType;
            m_RemoteID = RemoteID;
        }

        public string m_IP;
        public int m_Port;
        public bool m_IsEncrypt;
        public uint m_LocalType;
        public uint m_LocalID;
        public uint m_RemoteType;
        public uint m_RemoteID;
    }

    public class MxNwkConnecter : IDisposable
     {
         public MxNwkConnecter()
         {
             m_onConnected = new AsyncCallback(ConnectEnd);
         }

         internal class AsyncState
         {
             public readonly IPEndPoint ip;

             public AsyncState(IPEndPoint ip)
             {
                 this.ip = ip;
             }
         }

         public virtual bool Create(MxNwkConnecterDesc Desc, MxNwkObsConnecter ObsConnecter, MxNwkObsChannel ObsChannel, bool IsReconnect)
         {
             m_Desc = Desc;
             m_ObsConnecter = ObsConnecter;
             m_ObsChannel = ObsChannel;
             m_IsReconnect = IsReconnect;
             IPAddress ip = IPAddress.Parse(m_Desc.m_IP);
             IPEndPoint ipe = new IPEndPoint(ip, m_Desc.m_Port);
             m_sockConnecter = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
             PostConnect(ipe);
             return true;
         }

         private void ConnectEnd(IAsyncResult asyncResult)
         {
             AsyncState state = asyncResult.AsyncState as AsyncState;
             try
             {
                 m_sockConnecter.EndConnect(asyncResult);
                 SetConnectState(CS_CONNECT);
                 SetConnectProcState(CNT_CLOSED);

                 MxNwkChannelDesc descChl = new MxNwkChannelDesc();
                 descChl.hChannel = MxNetworkManager.GetInstance().AssignChannelHandle();
                 descChl.Sock = m_sockConnecter;
                 descChl.Remote = new MxNwkAddressIP(m_Desc.m_IP);

                 CreateChannel(descChl);
                 m_reconnectTimes = 0;
             }             
             catch (SocketException)
             {
                 ++m_reconnectTimes;
                 if (m_reconnectTimes < 10)
                 {
                     PostConnect(state.ip);
                     Console.WriteLine("Reconnect, IP:{0}!", state.ip.ToString());
                 }
                 else
                 {
                     m_ObsConnecter.OnConnectFailed();
                     m_reconnectTimes = 0;
                 }                 
             }             
         }

         private void PostConnect(IPEndPoint EndPoint)
         {
             m_sockConnecter.BeginConnect(EndPoint, m_onConnected, new AsyncState(EndPoint));
         }

         public void Dispose()
         {
             if (m_sockConnecter != null)
             {
                 m_sockConnecter.Close();
                 m_sockConnecter = null;
             }
         }

         bool CreateChannel(MxNwkChannelDesc pDesc)
         {
             MxNwkChannel tmpChannel = new MxNwkChannel();
             pDesc.IsEncrypt = m_Desc.m_IsEncrypt;
             pDesc.LocalType = m_Desc.m_LocalType;
             pDesc.LocalID = m_Desc.m_LocalID;
             pDesc.RemoteType = m_Desc.m_RemoteType;
             pDesc.RemoteID = m_Desc.m_RemoteID;
             pDesc.IsClient = true;
             TimeSpan tmpTime = new TimeSpan(DateTime.Now.Ticks);
             pDesc.CreateTimeSec = tmpTime.Seconds;
             pDesc.ConnectorID = m_ObsConnecter.Handle;
	         if(!tmpChannel.Create(pDesc, m_ObsChannel))
	         {
                 tmpChannel.Dispose();
		         return false;
	         }
	         m_ChannelHandle = pDesc.hChannel;
		        
	        if(tmpChannel.FirstRecv() != true)
	        {
		        return false;
	        }
	        

	        {
                MxNwkPacket pPkt = new MxNwkPacket(MxNwkPacket.MxNwkPacketType.NPT_MESSAGE);
		        pPkt.SetPacketID(1);
		        pPkt.Write(pDesc.LocalType); 
                pPkt.Write(pDesc.LocalID);
		        if(tmpChannel.SendMsg(pPkt) != true)
		        {			        
			        return false;
		        }		        
	        }

            MxNetworkManager.GetInstance().AddTmpChannel(tmpChannel);
            return true;
         }
         
         public int GetConnectState()
         {
             return m_connectState;
         }

         public void ResetAllConnectState(int state)
         {
             Interlocked.Exchange(ref m_connectStatePrev, state);
             Interlocked.Exchange(ref m_connectState, state);
         }

         public void SetConnectState(int state)
         {
             Interlocked.Exchange(ref m_connectStatePrev, m_connectState);
             Interlocked.Exchange(ref m_connectState, state);
         }

         
         public int GetConnectProcState()
         {
             return m_procConnectState;
         }

         public void SetConnectProcState(int state)
         {
             Interlocked.Exchange(ref m_procConnectState, state);
         }	

         private readonly AsyncCallback m_onConnected;
         protected MxNwkConnecterDesc m_Desc;
         protected MxNwkObsConnecter m_ObsConnecter;
         protected MxNwkObsChannel m_ObsChannel;
         protected int m_ChannelHandle;
         protected bool m_IsReconnect;
         protected Socket m_sockConnecter;
         protected /*volatile*/ int m_connectState;
         protected /*volatile*/ int m_connectStatePrev;
         protected /*volatile*/ int m_procConnectState;
         protected /*volatile*/ int m_procWorkerState;

         public const int CNT_RUNNING = 0;
         public const int CNT_REQUEST_CLOSE = 1;
         public const int CNT_CLOSED = 2;
         public const int CNT_MAX = 3;

         public const int CS_NONE = 0;
         public const int CS_CONNECT = 1;
         public const int CS_DISCONNECT = 2;
         public const int CS_FAILD = 3;
         public const int CS_MAX = 4;
         public int m_reconnectTimes = 0;
     }
}
